package com.hejia.alauda.service;

import com.hejia.alauda.entity.Balance;
import com.hejia.alauda.entity.Financing;
import com.hejia.alauda.entity.Order;
import com.hejia.alauda.entity.OrderSerial;
import com.hejia.alauda.entity.Order.Status;
import com.hejia.alauda.entity.Product;
import com.hejia.alauda.entity.example.FinancingExample;
import com.hejia.alauda.entity.example.OrderExample;
import com.hejia.alauda.event.EventConstant;
import com.hejia.alauda.event.MessageEvent;
import com.hejia.alauda.exception.BusinessException;
import com.hejia.alauda.jms.AlaudaEvent;
import com.hejia.alauda.jms.AlaudaSender;
import com.hejia.alauda.persistence.mybatis.BalanceMapper;
import com.hejia.alauda.persistence.mybatis.FinancingMapper;
import com.hejia.alauda.persistence.mybatis.OrderMapper;
import com.hejia.alauda.persistence.mybatis.ProductMapper;
import com.hejia.alauda.utils.Pager;
import com.hejia.alauda.utils.RateUtil;
import com.hejia.alauda.utils.Utils;

import org.apache.commons.lang3.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.eclipse.jetty.util.StringUtil;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;

import java.math.BigDecimal;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Executor;

import static com.hejia.alauda.utils.RateUtil.calculateSettlementDate;
import static org.springframework.util.CollectionUtils.isEmpty;
import static com.hejia.alauda.event.EventConstant.REDIS_KEY_PAYING_ORDER_NUMBER;

@Service
public class OrderServiceImpl implements OrderService {

	protected final Log logger = LogFactory.getLog(getClass());

	@Resource
	private OrderMapper orderMapper;

	@Resource
	private ProductMapper productMapper;

	@Resource
	private FinancingMapper financingMapper;

	@Resource
	private BalanceMapper balanceMapper;

	@Resource
	private UserService userService;

	@Resource
	private CreditService creditService;

	@Resource
	private CommonService commonService;

	@Resource
	private MongoTemplate mongoTemplate;

	@Override
	public Pager<Order> findOrders(OrderExample example, Pager<Order> pager) {
		pager.setList(orderMapper.selectByExample(example, pager));
		pager.setTotalCount(orderMapper.countByExample(example));
		return pager;
	}

	@Override
	public Order findOrderById(long id) {
		return orderMapper.selectByPrimaryKey(id);
	}

	@Override
	public Pager<Order> findOrders(String keyword, Date startDate,
			Date endDate, Pager<Order> pager) {

		return this.findOrders(keyword, null, null, startDate, endDate, pager);
	}

	@Override
	public Pager<Order> findOrders(String keyword, Status status,
			String finastatus, Date startDate, Date endDate, Pager<Order> pager) {

		Map<String, Object> map = new HashMap<String, Object>();

		if (StringUtil.isNotBlank(keyword)) {
			map.put("keyword", keyword);
		}
		if (status != null) {
			map.put("status", status);
		}
		if (finastatus != null) {
			map.put("finastatus", finastatus);
		}
		if (startDate != null) {
			map.put("startDate", startDate);
		}
		if (endDate != null) {
			map.put("endDate", endDate);
		}

		map.put("startNumber", pager.getStartNumber());
		map.put("pageSize", pager.getPageSize());

		pager.setList(this.orderMapper.selectOrders(map));
		pager.setTotalCount(this.orderMapper.selectOrdersCount(map));

		return pager;
	}

	public Pager<Order> findOrderByStatusAndDate(
			String orderNumber, 
			Order.Status status,
			String date, Pager<Order> pager) {
		OrderExample example = new OrderExample();
		
		if (StringUtils.isNotEmpty(orderNumber)) {
			example.appendCriterion("orderNumber=", orderNumber);
		}
		if (status != null) {
			example.appendCriterion("status=", status);
		}
		example.appendCriterion("str_to_date(createdate,'%Y-%m-%d')=", date);

		List<Order> list = orderMapper.selectByExample(example, pager);

		pager.setList(list);
		pager.setTotalCount(orderMapper.countByExample(example));
		return pager;
	}
	
	public Pager<Order> findOrderByStatusAndDate(
			Order.Status status,
			String date, Pager<Order> pager) {
		

		return findOrderByStatusAndDate(null, status,date, pager);
	}


	public Map<String, Object> findSumSalesAndOrders(Order.Status status,
			String createDate) {
		return orderMapper.dayTotalSalesAndOrders(status, createDate);
	}

	@SuppressWarnings("unchecked")
	@Override
	@Transactional
	public boolean insertOrder(Order order, Executor executor,
			final AlaudaSender sender) {
		OrderExample example = new OrderExample();
		example.appendCriterion("user_id=", order.getUser().getId());
		example.appendCriterion("status=", Order.Status.STATUS_UNPAY);
		// example.appendCriterion("product_id=", order.getProduct().getId());
		List<Order> list = orderMapper.selectByExample(example, null);
		if (!isEmpty(list)) {
			for (Order item : list) {
				final Long id = item.getId();
				/* 查询该订单是否已经进入了支付模式 */
				Object lock = commonService
						.getOpsForValue(REDIS_KEY_PAYING_ORDER_NUMBER
								+ item.getOrderNumber());
				if (lock == null) {
					int result = orderMapper
							.updateOrderStatusOverdueImmediately(item.getId());
					if (result > 0) {
						productMapper.updateAfterReleaseOrder(item.getAmount(),
								item.getProduct().getId());
						if (executor != null & sender != null) {
							executor.execute(new Runnable() {
								@Override
								public void run() {
									AlaudaEvent event = new AlaudaEvent();
									event.setEvent(AlaudaEvent.Event.EVENT_BUY_SUCCESS);
									event.setText(Utils
											.toJSON(findOrderById(id)));
									sender.send(event,
											AlaudaEvent.HEJIA_ORDER_EVENT_TOPIC);
								}
							});
						}
					}
				}
			}
		}
		if (productMapper.updateAfterInsertOrder(order.getAmount(), order
				.getProduct().getId()) > 0) {
			orderMapper.insert(order);
			return true;
		}
		return false;
	}

	@Override
	@Transactional
	public Order dealCancelOrder(String no_order) {
		OrderExample example = new OrderExample();
		example.appendCriterion("orderNumber=", no_order);
		Order order = orderMapper.selectOneByExample(example);
		if (order != null) {
			int result = orderMapper.updateOrderStatusOverdueImmediately(order
					.getId());
			if (result > 0) {
				productMapper.updateAfterReleaseOrder(order.getAmount(), order
						.getProduct().getId());
				return orderMapper.selectByPrimaryKey(order.getId());
			}
		}
		return null;
	}

	@Override
	@Transactional
	public boolean dealOverdueOrder(Long id) {
		Order order = orderMapper.selectByPrimaryKey(id);
		int result = orderMapper.updateOrderStatusOverdue(id);
		if (result > 0) {
			productMapper.updateAfterReleaseOrder(order.getAmount(), order
					.getProduct().getId());
			/* return dealExcessOrder(order.getProduct().getId()); */
			return true;
		}
		return false;
	}

	/**
	 * 如果订单释放的时候有多余的份额就给超额支付的用户使用
	 * 
	 * @param product_id
	 * @return
	 */
	public boolean dealExcessOrder(Long product_id) {
		OrderExample example = new OrderExample();
		example.appendCriterion("`product_id`=", product_id);
		example.appendCriterion("`status`=", Order.Status.STATUS_EXCESS);
		List<Order> list = orderMapper.selectByExample(example, null);
		if (!isEmpty(list)) {
			for (Order item : list) {
				if (productMapper.updateDealExcessOrder(item.getAmount(), item
						.getProduct().getId()) > 0) {
					updateOrderStatusImmediately(item.getOrderNumber(),
							Order.Status.STATUS_PAID.toString(),
							Order.Status.STATUS_EXCESS.toString());
					Product product = productMapper.selectByPrimaryKey(item
							.getProduct().getId());
					logger.info("STATUS_EXCESS===================>product_id="
							+ product.getId() + "&orderNumber="
							+ item.getOrderNumber() + "&orderAmount="
							+ item.getAmount() + "&productUnpayAmount="
							+ product.getUnpayAmount() + "&productSoldAmount="
							+ product.getSoldAmount());
					// 增加人数
					productMapper.updateNumberOfShoppers(product);
					// 添加金融包 以后的计算利息就靠它了
					Financing financing = new Financing();
					financing.setProduct(product);
					financing.setName(product.getName());
					financing.setTerm(product.getTerm());
					financing.setMinTerm(product.getMinTerm());
					financing.setInterestRate(product.getFinalRate());
					financing.setValueDate(product.getRealValueDate());
					// financing.setValueDate(item.getCreateDate().getTime() <
					// product.getValueDate().getTime() ? product.getValueDate()
					// : item.getCreateDate());
					financing.setAmount(item.getAmount());
					financing.setUser(item.getUser());
					financing.setCreateDate(RateUtil.getDate());
					// financing.setCreateDate(item.getCreateDate());
					financing.setModifyDate(financing.getCreateDate());
					financing.setSettlementDate(calculateSettlementDate(
							financing.getValueDate(), financing.getTerm()));
					financing.setStatus(Financing.Status.STATUS_DEFAULT);
					financing.setOrder(item);
					financing.setPayGateCode(item.getPayGateCode());
					financingMapper.insert(financing);
					// 计算每日的利息
					// createDayInterest(financing);
					// 处理用户个人资产
					Balance balance = balanceMapper.selectByPrimaryKey(item
							.getUser().getId());
					balance.setAsset(balance.getAsset() == null ? item
							.getAmount() : balance.getAsset().add(
							item.getAmount()));
					balance.setModifyDate(new Date());
					balanceMapper.updateByPrimaryKey(balance);
					// 增加积分
					creditService.calculateProductCredits(financing);
					// 发送短信
					userService.sendSuccessMessage(financing);
					// 发送邀请人的积分
					// userService.sendInvitationMessage(financing);

					MessageEvent event = new MessageEvent(
							EventConstant.KEY_PAY_SUCCESS_EVENT,
							financing.getId());
					CommonServiceImpl.applicationContext.publishEvent(event);
				}
			}
		}
		return true;
	}

	@Transactional
	@Override
	public boolean updateOrder(Order order) {
		return this.orderMapper.updateByPrimaryKey(order) > 0;
	}

	@Override
	public Order findOrderByPayNumber(String payNumber) {
		OrderExample exp = new OrderExample();
		exp.appendCriterion("payNumber=", payNumber);

		return orderMapper.selectOneByExample(exp);
	}

	@Override
	public List<Order> findOrdersByExample(OrderExample example,
			Pager<Order> pager) {
		if (example == null)
			example = new OrderExample();
		return orderMapper.selectByExample(example, pager);
	}

	@Override
	public int updateOrderStatusImmediately(String orderNumber,
			String changeStatus, String whereStatus) {
		return orderMapper.updateOrderStatusImmediately(orderNumber,
				changeStatus, whereStatus);
	}

	@Override
	public int updateOrderTag(Order order) {
		return orderMapper.updateOrderTag(order);
	}

	@Override
	public Order findOneOrder(OrderExample example) {
		return this.orderMapper.selectOneByExample(example);
	}

	@Override
	public Integer findOrderCountByExample(OrderExample example) {
		return orderMapper.countByExample(example);
	}

	@Override
	public Order updateOrderPayGateCode(Long id, String payGateCode)
			throws BusinessException {
		Order order = this.orderMapper.selectByPrimaryKey(id);
		order.setPayGateCode(payGateCode);
		if (this.orderMapper.updateByPrimaryKey(order) < 1) {
			throw new BusinessException("更新订单支付网关代码失败");
		}

		return order;
	}

	@Override
	public Date getOrderSettlementDate(Long id) {
		return orderMapper.getOrderSettlementDate(id);
	}

	@Override
	public Pager<Order> findExcessOrders(Pager<Order> pager, String keyword,
			Date date, Boolean refund) {

		if (keyword != null) {
			keyword = "%" + keyword + "%";
		}
		pager.setList(this.orderMapper.findExcessOrders(pager.getStartNumber(),
				pager.getPageSize(), keyword, date, refund));
		pager.setTotalCount(this.orderMapper.countExcessOrders(keyword, date,
				refund));

		return pager;
	}

	@Override
	public List<Long> findNotSyncPayStatusOrderIds(Date startTime) {

		return this.orderMapper.findNotSyncPayStatusOrderIds(startTime);
	}

	@Override
	public List<Order> findPaidOrdersByUserIdAndDate(Long userId, Date date) {
		return this.orderMapper.findPaidOrdersByUserIdAndDate(userId, date);
	}

	@Override
	public void saveOrderTrack(Order order, String track_id) {
		OrderSerial os = mongoTemplate.findOne(
				new Query(new Criteria("serial").is(order.getOrderNumber())),
				OrderSerial.class);
		if (os != null) {
			os.setTrack_id(track_id);
			mongoTemplate.save(os);
		}
	}

	@Override
	public Order refund(Long orderId) {
		Order order = this.orderMapper.selectByPrimaryKey(orderId);
		if (order.getRefundDate() != null) {
			return order;
		}

		order.setRefundDate(new Date());
		this.orderMapper.updateByPrimaryKey(order);
		return order;
	}

	@Override
	public Financing findFinancingByOrderId(Long id) {
		FinancingExample fiExample = new FinancingExample();
		fiExample.appendCriterion("order_id=", id);
		return financingMapper.selectOneByExample(fiExample);
	}

	@Override
	public BigDecimal findBonusByOrderId(Long id) {
		return financingMapper.findBonusByOrderId(id);
	}

	@Override
	public Pager<Order> findPaidOrdersByProduct(Long id, Pager<Order> pager) {
		if(pager == null)
			pager = new Pager<Order>();
		
		OrderExample example = new OrderExample();
		example.appendCriterion("product_id=", id);
		example.appendCriterion("status=", "STATUS_PAID");
		List<Order> orderList = orderMapper.selectByExample(example, pager);
		pager.setList(orderList);
		return pager;
	}

}
